import { ICleanService } from './../common/dataClean/cleanManager';
import { Order } from './../domain/model/order';
import { Kitchensolution } from './../domain/model/kitchensolution';
import { KitchenOrderItem } from './../domain/model/kitchenOrder';
import { EntityCopier } from 'tiny-entity';
import { CartItem } from './../domain/model/cart';
import { DataContextFactory } from './../domain/fbsDataContext';
import { KitchenOrder } from "../domain/model";
import { ServiceException } from "./serviceException";
import * as _ from "lodash";
import { Guid, Logger, ObjectCopier, intersectionArray, uniqueArray, differenceArray } from "../common/utils";
import { Transaction } from "tiny-entity";
import assert = require("assert");
import * as moment from "moment";
import * as R from "ramda";

export class KitchenOrderService implements ICleanService {
    private get ctx() {
        return DataContextFactory.GetDataContext();
    }

    get ServiceName() {
        return "kitchenOrderService";
    }

    async CleanData() {
        let ctx = DataContextFactory.GetDataContext();
        const time = moment().startOf('day').subtract(1, 'days').valueOf();
        const result = await ctx.KitchenOrder.ToList();
        const orders = result.filter(x => x.receiveTime < time);
        for (let data of orders) {
            await this.ctx.Delete(data);
        }
    }

    constructor() {
    }

    /**
     *新加入一个订单
    * 
    * @param {KitchenOrder} kitchenOrder
    * @returns
    * 
    * @memberOf KdsOrderService
    */
    public async createKitchenOrder(kitchenOrder: KitchenOrder): Promise<KitchenOrder> {
        let order = new KitchenOrder();
        //设置下单时间
        order = order.clone(kitchenOrder, order);
        order.receiveTime = new Date().getTime();
        order.id = Guid.GetGuid();
        return await this.ctx.Create(order)
    }


    public async getKitchenOrderById(id: string) {
        const order = await this.ctx.KitchenOrder.First(x => x.id == id, ["id"], [id])
        return order;
    }

    //出品菜单项
    public async expediteOrderByItemIds(id: string, itemIds: string[]) {
        const order = await this.ctx.KitchenOrder.First(x => x.id == id, ["id"], [id]);
        if (!order) {
            throw new ServiceException(this, "没有找到相关订单");
        };
        const items = order.items.filter(x => itemIds.indexOf(x.id) > -1);
        if (items.length != itemIds.length) {
            throw new ServiceException(this, "存在某些单项不存在");
        }
        const now = new Date().getTime();
        order.items.filter(x => itemIds.indexOf(x.id) > -1).forEach(x => x.deliverTime = now);
        const retOrder = await this.ctx.Update(order);
        return retOrder;
    }

    //取消出品
    public async cancelExpediteOrderByItemIds(id: string, itemIds: string[]) {
        const order = await this.ctx.KitchenOrder.First(x => x.id == id, ["id"], [id]);
        if (!order) {
            throw new ServiceException(this, "没有找到相关订单");
        };
        const items = order.items.filter(x => itemIds.indexOf(x.id) > -1);
        if (items.length != itemIds.length) {
            throw new ServiceException(this, "存在某些单项不存在");
        }
        const now = new Date().getTime();
        order.items.filter(x => itemIds.indexOf(x.id) > -1).forEach(x => x.deliverTime = 0);
        const retOrder = await this.ctx.Update(order);
        return retOrder;
    }

    public async closeOrderByItemIds(id: string, itemIds: string[]) {
        const order = await this.ctx.KitchenOrder.First(x => x.id == id, ["id"], [id]);
        if (!order) {
            throw new ServiceException(this, "没有找到相关订单");
        };
        const now = new Date().getTime();
        const items = order.items.filter(x => itemIds.indexOf(x.id) > -1);
        if (items.length != itemIds.length) {
            throw new ServiceException(this, "存在某些单项不存在");
        }
        //不能关闭的单项
        const needRefundedItems = items.filter(x => x.qty != x.refundQty);
        if (needRefundedItems.length > 0) {
            throw new ServiceException(this, needRefundedItems.map(x => x.id).join("、") + "不能被关闭");
        }
        order.items.filter(x => itemIds.indexOf(x.id) > -1).forEach(x => x.type = "closed");
        const retOrder = await this.ctx.Update(order);
        return retOrder;
    }

    /**
     * 完成订单的某些项
     * 
     * @param {string} id
     * @param {string[]} itemsIds
     * @returns
     * 
     * @memberOf KitchenOrderService
     */
    public async finishKitchenOrderByItemIds(id: string, itemsIds: string[]) {
        const order = await this.ctx.KitchenOrder.First(x => x.id == id, ["id"], [id]);
        if (!order) {
            throw new ServiceException(this, "找到相关订单");
        };
        const now = new Date().getTime();
        const items = order.items.filter(x => itemsIds.indexOf(x.id) > -1);
        if (items.length != itemsIds.length) {
            throw new ServiceException(this, "存在某些单项不存在");
        }
        //已经出品的单项
        const deliveredItems = items.filter(x => x.deliverTime);
        if (deliveredItems.length > 0) {
            throw new ServiceException(this, deliveredItems.map(x => x.id).join("、") + " 已经出品了");
        }
        //没有完成的单项
        const finishedItems = items.filter(x => !x.finishTime);
        if (finishedItems.length > 0) {
            throw new ServiceException(this, finishedItems.map(x => x.id).join("、") + " 为完成");
        }
        order.items.filter(x => itemsIds.indexOf(x.id) > -1).forEach(x => x.deliverTime = now);
        const retOrder = await this.ctx.Update(order);
        return retOrder;
    }

    private async checkOrderById(id) {
        const order = await this.ctx.KitchenOrder.First(x => x.id == id, ["id"], [id]);
        if (!order) {
            throw new ServiceException(this, "未找到相关订单");
        }
        return order;
    }
    private checkoutOrderItems(order, itemIds) {
        if (intersectionArray(order.items.map(x => x.id), itemIds).length != itemIds.length) {
            throw new ServiceException(this, differenceArray(order.items.map(x => x.id), itemIds).join("、") + "没有找到")
        }
    }
    private async checkOrderAndCartItems(orderId, itemIds: string[]) {
        const order = await this.checkOrderById(orderId);
        this.checkoutOrderItems(order, itemIds);
        return order;
    }
    public async finishKitchenOrderItemsByItemIds(id: string, orderItemIds: string[]) {
        const order = await this.checkOrderAndCartItems(id, orderItemIds);
        const orderItems = order.items.filter(x => orderItemIds.indexOf(x.id) > -1);
        const now = new Date().getTime();
        orderItems.forEach(v => v.finishTime = now);
        await this.ctx.Update(order);
        return order;
    }
    public async finishKitchenOrderItem(id: string, cartItemId: string) {
        const order = await this.ctx.KitchenOrder.First(x => x.id == id, ["id"], [id]);
        if (!order) {
            throw new ServiceException(this, "未找到相关订单");
        }
        let cartItem = order.items.find(x => x.id == cartItemId);
        if (!cartItem) {
            throw new ServiceException(this, "没有这个菜单项");
        }
        if (cartItem.finishTime) {
            throw new ServiceException(this, "订单项已经完成，不能操作这个菜单项")
        }
        if (cartItem.deliverTime) {
            throw new ServiceException(this, "该订单项已经出品，不能操作该订单项");
        }
        order.items.find(x => x.id == cartItemId).finishTime = new Date().getTime();
        await this.ctx.Update(order);
        return order;
    }

    public async unFinishKitchenOrderItem(id: string, cartItemId: string) {
        const order = await this.ctx.KitchenOrder.First(x => x.id == id, ["id"], [id]);
        if (!order) {
            throw new ServiceException(this, "找到相关订单");
        }
        let cartItem = order.items.find(x => x.id == cartItemId);
        if (!cartItem) {
            throw new ServiceException(this, "没有这个菜单项");
        }
        if (!cartItem.finishTime) {
            throw new ServiceException(this, "订单项没有完成，不能操作这个菜单项");
        }
        if (cartItem.deliverTime) {
            throw new ServiceException(this, "该订单项已经出品，不能操作该订单项");
        }
        order.items.find(x => x.id == cartItemId).finishTime = 0;
        await this.ctx.Update(order);
        return order;
    }

    public async getAllKitchenOrders() {
        return await this.ctx.KitchenOrder.ToList();
    }

    /**
     * 查询订单接口
     * 
     * @param {any} params
     * @returns {Promise<KitchenOrder[]>}
     * 
     * @memberOf KitchenOrderService
     */
    public async queryKitchenOrders(query: {
        stalls?: string[];
        finishTime?: string;
        beginFinishTime?: number;
        endFinishTime?: number;
        limit?: number;
        sortby?: string;
        order?: string;
    }): Promise<KitchenOrder[]> {
        //查询条件 
        const ctx = this.ctx;
        let result = await ctx.KitchenOrder.ToList();
        // let stallIdsString: any = [];
        // if (query.stalls) {
        //     for (let s of query.stalls) {
        //         stallIdsString.push(`"${s}"`);
        //     }
        //     stallIdsString = stallIdsString.join(',')
        // };

        // let sqlWhereString = '';
        // if (stallIdsString) {
        //     sqlWhereString = ` where stall like '%${stallIdsString}%' `;
        // }

        // let sql = `select * from kitchenorder ${sqlWhereString}`;
        // let result: any = await ctx.Query(<any>sql);

        // result = result.map(function(o) {
        //     let obj = EntityCopier.Copy(o, new KitchenOrder());
        //     return obj;
        // })


        // 1. 通过档口 ids 查询厨房单
        if (typeof query.stalls != "undefined") {
            result = result.filter(x =>
                intersectionArray(query.stalls, x.stall).length > 0
            );
        }
        //todo 现在只有未完成的
        if (typeof query.finishTime != "undefined") {
            result = result.filter(x => Boolean(
                this.getValidOrderItemsByStalls(x, query.stalls).some(x => !x.deliverTime)
            ));
        }
        if (typeof query.beginFinishTime != "undefined") {
            result = result.filter(x => Boolean(
                this.getValidOrderItemsByStalls(x, query.stalls).every(x => x.deliverTime > query.beginFinishTime)
            ));
        }
        if (typeof query.endFinishTime != "undefined") {
            result = result.filter(x => Boolean(
                this.getValidOrderItemsByStalls(x, query.stalls).every(x => x.deliverTime < query.endFinishTime)
            ));
        }
        if (query.sortby == "finishTime" && query.limit) {
            result.forEach(v => {
                v.finishTime = Math.max.apply(null, v.items.filter(x => {
                    return intersectionArray(query.stalls, x.stalls).length > 0 && x.deliverTime;
                }).map(x => x.deliverTime));
            })
            result.sort((x, y) => y.finishTime - x.finishTime);
            result.splice(query.limit, result.length);
        }
        return result;
    }

    /**
     * 获取items
     * 
     * @private
     * @param {KitchenOrder} kitchenOrder
     * @param {string[]} stalls
     * @returns {KitchenOrderItem[]}
     * 
     * @memberOf KitchenOrderServiceorderService
     */
    private getValidOrderItemsByStalls(kitchenOrder: KitchenOrder, stalls: string[]): KitchenOrderItem[] {
        const items = this.getOrderItemsByStalls(kitchenOrder, stalls);
        return items.filter(x => x.type != "closed" && x.type != "canceled");
    }

    private getOrderItemsByStalls(kitchenOrder: KitchenOrder, stalls: string[]): KitchenOrderItem[] {
        if (!stalls) {
            return kitchenOrder.items;
        }
        if (typeof kitchenOrder.items == 'string') {
            kitchenOrder.items = JSON.parse(kitchenOrder.items);
        }
        const items = kitchenOrder.items.filter(x =>
            intersectionArray(x.stalls, stalls).length > 0);
        return items;
    }


    public initKitchenOrderStalls(kitchenOrder: KitchenOrder, kitchensolutions: Kitchensolution[]): KitchenOrder {
        //设置每个item的stall
        kitchenOrder.items.forEach(orderItem => {
            const categoryIds = orderItem.categories.map(x => x.id);
            orderItem.stalls = kitchensolutions.filter(solution => {
                if (solution.id == "all") {
                    return true;
                }
                const kitchensolutionCategoryIds = solution.categories.map(x => x.id);
                return intersectionArray(kitchensolutionCategoryIds, categoryIds).length > 0
            }
            ).map(x => x.id)
        });
        //设置订单的stall

        const orderStalls = _.uniq(_.flatMap(kitchenOrder.items, x => x.stalls));
        kitchenOrder.stall = orderStalls;
        return kitchenOrder;
    }

    public initKitchenOrderLocalStall(kitchenOrder: KitchenOrder, stalls: string[]): KitchenOrder {
        kitchenOrder.items.forEach(item => {
            intersectionArray(item.stalls, stalls).length ? (<any>item).isLocalStall = true : (<any>item).isLocalStall = false;
        })
        return kitchenOrder;
    }

    /**
     * 订单检测
     * @param  {} order
     */
    private checKitchenOrder(kitchenOrder) {
        assert.notEqual(kitchenOrder, null, "下单失败，无订单信息");
        assert.notEqual(kitchenOrder.items, null, "下单失败，购物车无商品");
        assert.notEqual(kitchenOrder.orderId, null, "下单失败，无原始订单ID");
        assert.notEqual(kitchenOrder.seriaNo, null, "下单失败，无流水号信息");
        assert.notEqual(kitchenOrder.stall, null, "下单失败，无档口信息");
    }

    async updateKicthenOrderById(id, kitchenOrder: KitchenOrder) {
        const foundkitchenOrder = await this.getKitchenOrderById(id);
        foundkitchenOrder.customerIdentity = kitchenOrder.customerIdentity;
        foundkitchenOrder.extended = kitchenOrder.extended;
        const addItems = [];
        foundkitchenOrder.items.forEach(v => {
            const kitchenOrderItem = kitchenOrder.items.find(x => x.id == v.id);

            if (!kitchenOrderItem) return;
            //如果已经出品了就不修改数据了
            if (v.deliverTime) return;
            if (v.refundQty) return;
            // v.qty = kitchenOrderItem.qty;
            v.pickup = kitchenOrderItem.pickup;
            const refundQty = foundkitchenOrder.items.filter(item => item.id.indexOf(kitchenOrderItem.id) > -1).map(item => item.refundQty).reduce((x, y) => x + y, 0);
            if (v.qty > kitchenOrderItem.qty - kitchenOrderItem.refundQty) {
                addItems.push(this.generateKitchenOrderItemByOriginKitchenOrderItem(kitchenOrderItem, refundQty));
                v.qty = kitchenOrderItem.qty - kitchenOrderItem.refundQty;
            }
            //退完啦．
            if (v.qty == 0) {
                v.type = "canceled";
            }
            // if (v.qty == v.refundQty) {  //单项退完了，就默认给出品了
            //     v.deliverTime = new Date().getTime();
            // }
        });
        const cmp = (x, y) => x.id === y.id;
        foundkitchenOrder.items = R.concat(foundkitchenOrder.items, addItems);
        const updatedKitchenOrder = await this.ctx.Update(foundkitchenOrder);
        return updatedKitchenOrder;
    }


    async getKitchenOrderByKitchenOrderId(orderId) {
        const order = await this.ctx.KitchenOrder.First(x => x.orderId == orderId, ["orderId"], [orderId]);
        if (!order)
            throw new ServiceException(this, "orderId为 " + orderId + "的厨房单没找到");
        return order;
    }

    async putKitchenOrderByOrder(allKdsKitchensolutions, order: Order) {
        const kitchenOrder = this.generateKitchenOrderByOrder(this.preDisposeOrder(order));
        const resKitchenOrder = this.initKitchenOrderStalls(kitchenOrder, allKdsKitchensolutions);
        const founbdOrder = await this.findKitchenOrderByOrderId(order.id);
        if (founbdOrder) {
            return await this.updateKicthenOrderById(founbdOrder.id, resKitchenOrder);
        } else {
            try {
                return await this.createKitchenOrder(resKitchenOrder);
            } catch (error) {
                //应该使用锁解决 reader writer lock node js
                if ((error.message || error).indexOf("ER_DUP_ENTRY") > -1) {
                    console.log("=======>", error.message || error)
                    let order = await this.getKitchenOrderByKitchenOrderId(resKitchenOrder.orderId);
                    return order;
                }
            }
        }
    }

    async  findKitchenOrderByOrderId(orderId) {
        const hasOrder = await this.ctx.KitchenOrder.First(x => x.orderId == orderId, ["orderId"], [orderId])
        return hasOrder;
    }

    disposeKitchenOrder(order): KitchenOrder {
        delete order.orderNo;
        delete order.tallId;
        return order;
    }

    preDisposeOrder(order: Order): Order {
        //赠送会把拆的单子还原回去
        let relItems = [];
        const groupedItems = R.groupBy<CartItem>((item) => {
            return (item.extended && item.extended.refItemId) || item.id;
        })(order.cart.items);
        Object.keys(groupedItems).map(itemId => {
            const sourceItem = order.cart.items.find(x => x.id == itemId);
            sourceItem.qty = groupedItems[itemId].map(x => x.qty).reduce((x, y) => x + y, 0);
            sourceItem.refundQty = groupedItems[itemId].map(x => x.refundQty || 0).reduce((x, y) => x + y, 0);
            relItems.push(sourceItem);
        });
        order.cart.items = relItems;
        return order;
    }

    generateItemsByComboedCartItem(comboItem): Array<KitchenOrderItem> {
        const items = [];
        comboItem.extended.combo.group.forEach(groupTtem => {
            groupTtem.items.forEach(item => {
                const kdsDataItem = {
                    id: comboItem.id + item.inventoryId,
                    qty: item.qty * Number(comboItem.qty || 1),
                    refundQty: item.qty * Number(comboItem.refundQty || 0),
                    status: item.status,
                    inventoryCode: item.inventoryCode,
                    inventoryId: item.inventoryId,
                    inventoryName: item.inventoryName,
                    pickup: (<any>comboItem).extended.pickup,
                    description: ((<any>item).extended && (<any>item).extended.description || "") + (comboItem && comboItem.extended && comboItem.extended.description || ""),
                    categories: item.extended.categories.map(function (c) {
                        return {
                            id: c.id,
                            name: c.name
                        }
                    })
                };
                if (item.modifiers && item.modifiers.items) {
                    (<any>kdsDataItem).modifiers = item.modifiers.items.map(function (m) {
                        return {
                            name: m.name,
                            options: m.options.map(function (o) {
                                return {
                                    name: o.name,
                                    price: o.price
                                };
                            })
                        }
                    });
                } else {
                    (<any>kdsDataItem).modifiers = [];
                }
                items.push(kdsDataItem);
            })
        })
        return items;
    }

    //
    generateKitchenOrderItemByOriginKitchenOrderItem(originItem: KitchenOrderItem, refundQty): KitchenOrderItem {
        const kitchenOrderItem = JSON.parse(JSON.stringify(originItem));
        kitchenOrderItem.id += ("-refund-" + originItem.refundQty);
        kitchenOrderItem.createTime = new Date().getTime();
        kitchenOrderItem.qty = originItem.refundQty - refundQty;
        kitchenOrderItem.refundQty = kitchenOrderItem.qty;
        return kitchenOrderItem;
    }

    generateItemsByRefundCartItem(item): Array<KitchenOrderItem> {
        const items = [];
        const kitchenOrderitem = this.generateNomalItem(item);
        const refundItem = JSON.parse(JSON.stringify(kitchenOrderitem));
        refundItem.id += "-refund";
        refundItem.createTime = new Date().getTime();
        refundItem.qty = item.refundQty;
        refundItem.refundQty = item.refundQty;
        items.push(refundItem);
        kitchenOrderitem.qty = item.qty - item.refundQty;
        kitchenOrderitem.refundQty = 0;
        items.push(kitchenOrderitem);
        return items;
    }

    generateNomalItem(item): KitchenOrderItem {
        const itemForKds = {
            id: item.id,
            qty: item.qty,
            refundQty: item.refundQty || 0,
            status: item.status,
            inventoryCode: item.inventoryCode,
            inventoryId: item.inventoryId,
            inventoryName: item.inventoryName,
            description: (<any>item).extended.description,
            pickup: (<any>item).extended.pickup,
            categories: item.extended.categories.map(function (c) {
                return {
                    id: c.id,
                    name: c.name
                }
            })
        }
        if (item.modifiers && item.modifiers.items) {
            (<any>itemForKds).modifiers = item.modifiers.items.map(function (m) {
                return {
                    name: m.name,
                    options: m.options.map(function (o) {
                        return {
                            name: o.name,
                            price: o.price
                        };
                    })
                }
            });
        } else {
            (<any>itemForKds).modifiers = [];
        }
        return itemForKds;
    }

    generateKitchenOrderByOrder(order: Order): KitchenOrder {
        const kdsData = {
            orderId: order.id,
            seriaNo: order.serialNo,
            items: [],
            terminalBindId: '',
            terminalName: order.terminalName,
            terminalId: '',
            description: order.description,
            customerIdentity: this.getCustomerIdentityByOrder(order),
            extended: {
                table: order.extended.table
            }
        }
        kdsData.items = this.generateKitchenOrderItems(order.cart.items);
        return this.disposeKitchenOrder(kdsData)
    }

    generateKitchenOrderItems(items: Array<CartItem>): Array<KitchenOrderItem> {
        let resultItems = [];
        items.forEach((item) => {
            //套餐
            if ((<any>item).extended.combo && (<any>item).extended.combo.group && (<any>item).extended.combo.group.length > 0) {
                resultItems = resultItems.concat(this.generateItemsByComboedCartItem(item));
            } else {
                //普通的
                resultItems.push(this.generateNomalItem(item));
            }
        })
        return resultItems;
    }

    getCustomerIdentityByOrder(order) {
        if (order.extended && order.extended.tableParty && order.extended.tableParty.tableName) {
            return order.extended.tableParty.tableName;
        } else {
            return order.extended.numberPlate || order.serialNo;
        }
    }

    async remindOrdersByOrderIds(orderIds: Array<string>, items: Array<any>) {
        const KitchenOrderItemIds = this.getItemIdsByItems(items);
        const kitchenOrders = await Promise.all(orderIds.map(id => this.remindKitchenOrderByItemIds(id, KitchenOrderItemIds)));
        return kitchenOrders;
    }

    getItemIdsByItems(items) {
        return items.map(x => x.id);
    }

    async remindKitchenOrderByItemIds(orderId, itemIds: Array<string>) {
        const foundKitchenOrder = await this.getKitchenOrderByKitchenOrderId(orderId);
        itemIds.forEach(itemId => {
            const exitedItem = foundKitchenOrder.items.find(x => x.id == itemId);
            if (!exitedItem) return;
            exitedItem.remindCount = exitedItem.remindCount || 0;
            exitedItem.remindCount++;
        })
        return await this.ctx.Update(foundKitchenOrder);
    }
}
